/*
 * implementation of scheme object.
 */

#include <string.h>
#include <debug.h>
#include "schemeobject.h"
#include "toplevel.h"
#include "scheme.h"

#define PRINT_PETITE_STYLE 1 // DEBUG

#define UPPERCASE_LETTER_CHAR "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* not part of petite's symbol chars, for some reason */
#define LOWERCASE_LETTER_CHAR "abcdefghijklmnopqrstuvwxyz"
#define DIGIT_CHAR "01234567890"
#define GENERAL_SYMBOL_CHAR "!@$%^&*_-+=/?:<>,."
#define SYMBOL_CHARS (UPPERCASE_LETTER_CHAR LOWERCASE_LETTER_CHAR DIGIT_CHAR GENERAL_SYMBOL_CHAR)


#define STANDART_SCHEME_DATATYPE_LENGTH 32

#define MAX_INTEGER_LENGTH		STANDART_SCHEME_DATATYPE_LENGTH
#define MAX_CHAR_LENGTH			STANDART_SCHEME_DATATYPE_LENGTH
#define MAX_BOOLEAN_LENGTH		STANDART_SCHEME_DATATYPE_LENGTH
#define MAX_NIL_LENGTH			STANDART_SCHEME_DATATYPE_LENGTH
#define MAX_EMPTY_VECTOR_LENGTH STANDART_SCHEME_DATATYPE_LENGTH

#define NIL_PRINT_STRING			"()"
#define EMPTY_VECTOR_PRINT_STRING	"#0()"

#if PRINT_PETITE_STYLE
#define CLOSURE_PRINT_STRING		"#<procedure>"
#define VOID_PRINT_STRING			""
#else
#define CLOSURE_PRINT_STRING		"#<compiled closure>"
#define VOID_PRINT_STRING			"#<void object>"
#endif

/*** SCHEME OBJECT CREATION ***/

SchemeObject* makeSchemeObject(void)
{
	SchemeObject* sob;

	sob = (SchemeObject* )autoMalloc(sizeof(SchemeObject));
	SOB_DATA(sob) = NULL;

	return sob;
}

SchemeObject* makeSchemeVoid(void)
{
#if 0
	SchemeObject* sob;

	sob = makeSchemeObject();
	SOB_TYPE(sob) = SCHEME_VOID;
	SOB_DATA(sob) = NULL;

	return sob;
#else
	return schemeVoid ;
#endif
}

SchemeObject* makeSchemeInt(int n)
{
	SchemeObject* sob;

	sob = makeSchemeObject();
	SOB_TYPE(sob) = SCHEME_INT;
	SOB_DATA(sob) = SobData(autoMalloc(sizeof(SchemeIntData)));
	SOB_INT_VALUE(sob) = n;

	return sob;
}

SchemeObject* makeSchemeChar(char c)
{
	SchemeObject* sob;

	sob = makeSchemeObject();
	SOB_TYPE(sob) = SCHEME_CHAR;
	SOB_DATA(sob) = SobData(autoMalloc(sizeof(SchemeCharData)));
	SOB_CHAR_VALUE(sob) = c;

	return sob;
}

SchemeObject* makeSchemeBool(bool b)
{
#if 0
	SchemeObject* sob;

	sob = makeSchemeObject();
	SOB_TYPE(sob) = SCHEME_BOOL;
	SOB_DATA(sob) = SobData(autoMalloc(sizeof(SchemeBoolData)));

	SOB_BOOL_VALUE(sob) = b;

	return sob;
#else
	if (b)
		return schemeTrue ;
	else
		return schemeFalse ;
#endif
}

SchemeObject* makeSchemeString(string s)
{
	SchemeObject* sob;

	sob = makeSchemeObject();
	SOB_TYPE(sob) = SCHEME_STRING;
	SOB_DATA(sob) = SobData(autoMalloc(sizeof(SchemeStringData)));

	SOB_STRING_LENGTH(sob) = strlen(s);
	SOB_STRING_VALUE(sob) = (char *)autoMalloc(1 + SOB_STRING_LENGTH(sob));

	strcpy(SOB_STRING_VALUE(sob), s);

	return sob;
}

SchemeObject* makeSchemeStringFromScratch(int length, char filler)
{
	SchemeObject* sob;
	int i;

	RUNTIME_ASSERT(length >= 0);

	sob = makeSchemeObject();
	SOB_TYPE(sob) = SCHEME_STRING;
	SOB_DATA(sob) = SobData(autoMalloc(sizeof(SchemeStringData)));

	SOB_STRING_LENGTH(sob) = length;
	SOB_STRING_VALUE(sob) = (char *)autoMalloc(SOB_STRING_LENGTH(sob) + 1);

	for (i = 0; i < length; ++i) {
		SOB_STRING_SET(sob, i, filler);
	}

	SOB_STRING_SET(sob, length, '\0');

	return sob;
}

SchemeObject* makeSchemeNil(void)
{
#if 0
	SchemeObject* sob;
	sob = makeSchemeObject();
	SOB_TYPE(sob) = SCHEME_NIL;
	SOB_DATA(sob) = NULL;

	return sob;
#else
	return schemeNil ;
#endif
}

SchemeObject* makeSchemePair(SchemeObject* car, SchemeObject* cdr)
{
	SchemeObject* sob;

	sob = makeSchemeObject();
	SOB_TYPE(sob) = SCHEME_PAIR;
	SOB_DATA(sob) = SobData(autoMalloc(sizeof(SchemePairData)));

	SOB_PAIR_CAR(sob) = car;
	SOB_PAIR_CDR(sob) = cdr;

	return sob;
}

SchemeObject* makeSchemeVector(int n, SchemeObject* init)
{
	SchemeObject* sob;
	int i;

	RUNTIME_ASSERT(n >= 0);

	sob = makeSchemeObject();
	SOB_TYPE(sob) = SCHEME_VECTOR;
	SOB_DATA(sob) = (SchemeObjectData *)autoMalloc(sizeof(SchemeVectorData));

	SOB_VECTOR_LENGTH(sob) = n;

	SOB_VECTOR_VALUE(sob) =
	(SchemeObject* *)autoMalloc(n * sizeof(SchemeObject* ));

	for (i = 0; i < n; ++i) {
		SOB_VECTOR_SET(sob, i, init);
	}

	return sob;
}

SchemeObject* makeSchemeSymbol(string name)
{
	int len;
	SchemeObject* sob;

	len = strlen(name);
	RUNTIME_ASSERT(len > 0);

	sob = makeSchemeObject();
	SOB_TYPE(sob) = SCHEME_SYMBOL;
	SOB_DATA(sob) = SobData(autoMalloc(sizeof(SchemeSymbolData)));
	SOB_SYMBOL_ENTRY(sob) = getSymbol(name);

	return sob;
}

SchemeObject* makeSchemeClosure(struct SchemeObject*** env, MachineCode code)
{
	SchemeObject* sob;

	sob = makeSchemeObject();
	SOB_TYPE(sob) = SCHEME_CLOSURE;
	SOB_DATA(sob) = SobData(autoMalloc(sizeof(SchemeClosureData)));

	SOB_CLOSURE_ENV(sob) = env ;
	SOB_CLOSURE_CODE(sob) = code ;

	return sob;
}
/*** SCHEME OBJECT PRINTING ***/

string sobVoidToString(SchemeObject* sob);
string sobIntToString(SchemeObject* sob);
string sobCharToString(SchemeObject* sob);
string sobBoolToString(SchemeObject* sob);
string sobStringToString(SchemeObject* sob);
string sobNilToString(SchemeObject* sob);
string sobPairToString(SchemeObject* sob);
string sobVectorToString(SchemeObject* sob);
string sobSymbolToString(SchemeObject* sob);
string sobClosureToString(SchemeObject* sob);

string sobToString(SchemeObject* sob)
{
	SchemeType sobType;
	DEBUG_ASSERT(sob != NULL) ;

	sobType = SOB_TYPE(sob);
	DEBUG_ASSERT((sobType >= SCHEME_TYPE_FIRST) && (sobType < SCHEME_TYPE_END));

	switch (sobType)
	{
		case SCHEME_VOID:           return sobVoidToString(sob);
		case SCHEME_INT:            return sobIntToString(sob);
		case SCHEME_CHAR:           return sobCharToString(sob);
		case SCHEME_BOOL:           return sobBoolToString(sob);
		case SCHEME_STRING:         return sobStringToString(sob);
		case SCHEME_NIL:            return sobNilToString(sob);
		case SCHEME_PAIR:           return sobPairToString(sob);
		case SCHEME_VECTOR:         return sobVectorToString(sob);
		case SCHEME_SYMBOL:         return sobSymbolToString(sob);
		case SCHEME_CLOSURE:        return sobClosureToString(sob);
		default: DEBUG_YELL("bug!") ; return NULL ;
	}
}

string sobVoidToString(SchemeObject* sob)
{
	return stringCopy(VOID_PRINT_STRING);
}

string sobIntToString(SchemeObject* sob)
{
	char buf[MAX_INTEGER_LENGTH];
	sprintf(buf, "%d", SOB_INT_VALUE(sob));
	return stringCopy(buf) ;
}

string sobCharToString(SchemeObject* sob)
{
	char c, buf[MAX_CHAR_LENGTH];

	c = SOB_CHAR_VALUE(sob);

	if (c == ' ') {
		sprintf(buf, "#\\space");
	} else if (c == '\n') {
		sprintf(buf, "#\\newline");
	} else if (c == '\r') {
		sprintf(buf, "#\\return");
	} else if (c == '\f') {
		sprintf(buf, "#\\page");
	} else if (c < ' ') {
		int o1, o2, o3;

		o3 = c % 8; c = c >> 3;
		o2 = c % 8; c = c >> 3;
		o1 = c % 8; /* not needed; just for good luck :) */
		sprintf(buf, "#\\%d%d%d", o1, o2, o3);
	} else {
		sprintf(buf, "#\\%c", c);
	}

	return stringCopy(buf) ;
}

string sobBoolToString(SchemeObject* sob)
{
	char buf[MAX_BOOLEAN_LENGTH];
	int b;

	b = SOB_BOOL_VALUE(sob);
	switch (b)
	{
		case FALSE: sprintf(buf, "#f"); break;
		case TRUE: sprintf(buf, "#t"); break;
		default: DEBUG_YELL("bug!") ; buf[0] = '\0' ; break ;
	}

	return stringCopy(buf) ;
}

string sobStringToString(SchemeObject* sob)
{
	string src, dst, res;
	int srcLen, i, j;

	srcLen = SOB_STRING_LENGTH(sob);
	src = SOB_STRING_VALUE(sob);
	dst = (string)autoMalloc(1 + (srcLen << 1)); /* max possible dst length */

	for (i = 0, j = 0; i < srcLen; ++i)
	{
		switch (src[i])
		{
			case '\\': dst[j++] = '\\'; dst[j++] = '\\'; break;
			case '"': dst[j++] = '\\'; dst[j++] = '"'; break;
			default: dst[j++] = src[i]; break ;
		}
	}

	dst[j] = '\0';

	res = stringAlloc(3 + strlen(dst));
	strcpy(res, "\"");
	strcat(res, dst);
	strcat(res, "\"");

	return res;
}

string sobNilToString(SchemeObject* sob)
{
	char buf[MAX_NIL_LENGTH];
	sprintf(buf, NIL_PRINT_STRING);
	return stringCopy(buf) ;
}

string sobPairToString(SchemeObject* sob)
{
	string tmp1, tmp2, res;
	SchemeObject* cdr;

	tmp1 = sobToString(SOB_PAIR_CAR(sob));
	cdr = SOB_PAIR_CDR(sob);
	if (sobIsNil(cdr)) {
		res = stringAlloc(3 + strlen(tmp1));
		sprintf(res, "(%s)", tmp1); autoFree(tmp1);
	} else if (sobIsPair(cdr)) {
		tmp2 = sobToString(cdr);
		res = stringAlloc(3 + strlen(tmp1) + strlen(tmp2));
		sprintf(res, "(%s %s", tmp1, tmp2 + 1);
	} else {
		tmp2 = sobToString(cdr);
		res = stringAlloc(6 + strlen(tmp1) + strlen(tmp2));
		sprintf(res, "(%s . %s)", tmp1, tmp2);
		autoFree(tmp1); autoFree(tmp2);
	}

	return res;
}

string sobVectorToString(SchemeObject* sob)
{
	string res ;
	char tmp[MAX_EMPTY_VECTOR_LENGTH];
	int len, i;

	len = SOB_VECTOR_LENGTH(sob);
	if (len == 0)
	{
		res = stringAlloc(strlen(EMPTY_VECTOR_PRINT_STRING));
		strcpy(res, EMPTY_VECTOR_PRINT_STRING);

		return res;
	}
	else
	{
		string *eltStr;
		int resSize;

		eltStr = (string *)autoMalloc(len * sizeof(string ));

		for (i = 0, resSize = 2 + len; i < len; ++i)
		{
			eltStr[i] = sobToString(SOB_VECTOR_REF(sob, i));
			resSize += strlen(eltStr[i]);
		}

		sprintf(tmp, "#%d(", len);
		resSize += strlen(tmp);

		res = stringAlloc(resSize);
		strcpy(res, tmp);
		strcat(res, eltStr[0]); autoFree(eltStr[0]);

		for (i = 1; i < len; ++i)
		{
			strcat(res, " ");
			strcat(res, eltStr[i]); autoFree(eltStr[i]);
		}

		autoFree(eltStr);

		strcat(res, ")");

		return res;
	}
}

string sobSymbolToString(SchemeObject* sob)
{
	string res, tmp ;
	int len, i ;
	bool isSpecial = FALSE;


	tmp = SOB_SYMBOL_NAME(sob);
	len = strlen(tmp);

	for (i = 0, isSpecial = 0; i < len; ++i)
	{
		if (strchr(SYMBOL_CHARS, tmp[i]) == NULL)
		{
			isSpecial = 1;
			break;
		}
	}

	if (isSpecial)
	{
		res = stringAlloc(3 + len);
		sprintf(res, "|%s|", tmp);
	}
	else
	{
		res = stringAlloc(1 + len);
		sprintf(res, "%s", tmp);
	}

	return res;
}

string sobClosureToString(SchemeObject* sob)
{
	return stringCopy(CLOSURE_PRINT_STRING) ;
}
